package org.ycsoft.metroid;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnCancelListener;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.MenuItem;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.*;
import android.widget.AdapterView.OnItemClickListener;

import org.ycsoft.MetroidReader.*;

import java.io.IOException;
import java.util.*;

public class MetroidCentral extends Activity {

	private TextView lblTitle;
	private ListView listPath;

	private String departure, arrival;
	private String[] stopsList;

	SearchFields dlg;

	private MetroidReader reader;

	private String currentMap; // path of the currently loaded map
	private String mapTitle;

	private boolean debugMode = true;

	private ProgressDialog dlgProgress;
	private Handler handler;

	private boolean modeAffichageVerbose = false; // indique si tous les stops
	// du trajet sont affiché

	private int nbConnections = 0;

	public static final String TAG = "Metroid";
	public static final String KEY_DEPARTURE = "Departure";
	public static final String KEY_ARRIVAL = "Arrival";
	public static final String KEY_STOPS = "Stops";
	public static final int SEARCH_FIELDS = 1;
	public static final int SEARCH_ID=1;
	public static final int LIST_ID=2;
	public static final int CLOSE_ID=3;

	TextView footer;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		setContentView(R.layout.central);
		// Retrieve widgets in UI
		lblTitle = (TextView) findViewById(R.id.lblTitle);
		listPath = (ListView) findViewById(R.id.listPath);

		// images
		ImageButton btnSearch = (ImageButton) findViewById(R.id.btnSearch);
		btnSearch.setOnClickListener(searchListener);
		ImageButton btnLines = (ImageButton) findViewById(R.id.btnLines);
		btnLines.setOnClickListener(linesListener);
		ImageButton btnBack = (ImageButton) findViewById(R.id.btnFermer);
		btnBack.setOnClickListener(backListener);
		ImageButton btnInverse = (ImageButton) findViewById(R.id.btnInverse);
		btnInverse.setOnClickListener(inverseListener);

		// focus change handling
		footer = (TextView) findViewById(R.id.copy);
		btnSearch.setOnFocusChangeListener(new OnFocusChangeListener() {
			
			@Override
			public void onFocusChange(View v, boolean hasFocus) {
				// TODO Auto-generated method stub
				if (v.isFocused())
					footer.setText(R.string.search_load);
				
			}

		});

		btnLines.setOnFocusChangeListener(new OnFocusChangeListener() {


			@Override
			public void onFocusChange(View v, boolean hasFocus) {
				// TODO Auto-generated method stub
				if (v.isFocused())
					footer.setText(R.string.lines_load);
			}

		});

		btnBack.setOnFocusChangeListener(new OnFocusChangeListener() {
			
			@Override
			public void onFocusChange(View v, boolean hasFocus) {
				// TODO Auto-generated method stub
				if (v.isFocused())
					footer.setText(R.string.back_load);
			}

		});

		btnInverse.setOnFocusChangeListener(new OnFocusChangeListener() {
		

			@Override
			public void onFocusChange(View v, boolean hasFocus) {
				// TODO Auto-generated method stub
				if (v.isFocused())
					footer.setText(R.string.inverse_load);
			}

		});

		// listview
		ListView listPath = (ListView) findViewById(R.id.listPath);
		listPath.setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView l, View v, int position, long id) {
				((ItineraryListAdapter) l.getAdapter()).toggle(position);
			}
		});

		// Retrieve map path
		Bundle extras = getIntent().getExtras();

		if (extras != null) {
			currentMap = extras.getString(Commons.KEY_MAPFILE);
			mapTitle = extras.getString(Commons.KEY_MAPNAME);
		} else if (icicle != null && icicle.containsKey(Commons.KEY_MAPFILE)) {
			currentMap = icicle.getString(Commons.KEY_MAPFILE);
			mapTitle = icicle.getString(Commons.KEY_MAPNAME);
		} else {
			Log.e(TAG, "No mapFile parameter found");
			setResult(RESULT_CANCELED);
			finish();
			return;
		}

		if (icicle != null && icicle.containsKey(KEY_DEPARTURE)
				&& icicle.containsKey(KEY_ARRIVAL)
				&& icicle.containsKey(KEY_STOPS)) {
			departure = icicle.getString(KEY_DEPARTURE);
			arrival = icicle.getString(KEY_ARRIVAL);
			// stopsList = icicle.getStringArray(KEY_STOPS);

		}

		try {
			reader = new MetroidReader(currentMap);
			// set title
			lblTitle.setText(mapTitle);

		} catch (Exception ioe) {
			Log.e(TAG, ioe.getMessage() + "---" + currentMap+"--"+
					getResources().getString(R.string.error_maps).toString());
			// afficher msg d'erreur et quitter
			error(getResources().getString(R.string.error_maps).toString());
			setResult(RESULT_CANCELED);
			finish();
			return;
		}

		handler = new Handler();

		// Start lengthy map loading operation in a background thread
		Thread thread = new Thread(new Runnable() {
			public void run() {
				try {

					synchronized (handler) {
						handler.post(new Runnable() {
							public void run() {
								// afficher dialog progress
								dlgProgress = ProgressDialog.show(
										MetroidCentral.this, null,
										getResources().getString(
												R.string.loading_maps), true,
										false);
							}
						});
					}

					// load map data
					loadMap();

					// reload itinerary if necessary
					synchronized (handler) {
						handler.post(new Runnable() {
							public void run() {
								dlgProgress.dismiss();
							}
						});
					}

				} catch (Exception ex) {
					Log.e(TAG, ex.getMessage() != null ? ex.getMessage() : ex
							.toString(), ex);
					// post an alert and quits activity
					synchronized (handler) {
						handler.post(new Runnable() {
							public void run() {
								error(getResources().getString(
										R.string.error_maps).toString());
								finish();
							}
						});
					}

				}

			}
		});
		thread.start();
	}
	
	
	public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        menu.add(0, SEARCH_ID, 0, R.string.search_load);
        menu.add(0, LIST_ID,0, R.string.lines_load);
        menu.add(0, CLOSE_ID, 0,R.string.back_load);
        return true;
    }



public boolean onMenuItemSelected(int featureId, MenuItem item) {
        switch(item.getItemId()) {
        case SEARCH_ID:
        	showSearchFields();
            return true;
        case LIST_ID:
            startLinesList();
            return true;
        case CLOSE_ID:
        	finish();
        }
       
        return super.onMenuItemSelected(featureId, item);
    }

	@Override
	public void onDestroy() {
		try {
			if (reader != null && !reader.isStreamClosed()) {
				reader.closeStream();
			}
		} catch (Exception e) {
			Log.e(TAG, e.getMessage());
		}
		super.onDestroy();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Activity#onFreeze(android.os.Bundle)
	 */
	public void onFreeze(Bundle outState) {
		// save state
		outState.putString(Commons.KEY_MAPFILE, currentMap);
		outState.putString(Commons.KEY_MAPNAME, mapTitle);
		outState.putString(KEY_DEPARTURE, departure);
		outState.putString(KEY_ARRIVAL, arrival);

		//super.onFreeze(outState);
	}

	private OnClickListener searchListener = new OnClickListener() {
		public void onClick(View v) {
			showSearchFields();
		}
	};
	private OnClickListener searchResultListener = new OnClickListener() {
		public void onClick(View v) {
			departure = dlg.getDeparture();
			arrival = dlg.getArrival();
			dlg.dismiss();
			// hides home icon and metadata
			LinearLayout homeLay = (LinearLayout) findViewById(R.id.homeLayout);
			homeLay.setVisibility(View.GONE);
			doSearch();
		}
	};

	private void showSearchFields() {
		dlg = new SearchFields(this);
		dlg.setDeparture(departure);
		dlg.setArrival(arrival);
		dlg.setStopsList(stopsList);
		dlg.setOnClickListener(searchResultListener);
		dlg.show();
	}

	private OnClickListener linesListener = new OnClickListener() {
		public void onClick(View v) {
			startLinesList();
		}
	};
	
	public void startLinesList()
	{
		Intent intent = new Intent(MetroidCentral.this,
				org.ycsoft.metroid.LinesList.class);
		Log.e(TAG, "Calling LinesList");
		intent.putExtra(Commons.KEY_MAPFILE, currentMap);
		intent.putExtra(Commons.KEY_MAPNAME, mapTitle);
		// intent.putExtra(Commons.METROID_READER,reader);
		startActivity(intent);
		Log.e(TAG, "Call succeeded");
	}

	private OnClickListener backListener = new OnClickListener() {
		public void onClick(View v) {
			// close activity
			finish();
		}
	};

	private OnClickListener inverseListener = new OnClickListener() {
		public void onClick(View v) {
			// swap stations
			String tmp = departure;
			departure = arrival;
			arrival = tmp;
			doSearch();
		}
	};

	private void doSearch() {

		departure = departure.trim();
		arrival = arrival.trim();

		// si les stops sont vides, retourner
		if (departure.length() == 0 || arrival.length() == 0)
			return;

		// afficher dialog progress
		dlgProgress = ProgressDialog.show(MetroidCentral.this, null,
				getResources().getString(R.string.computing_path), true, false);

		// Start lengthy path loading operation in a background thread
		Thread thread = new Thread(new Runnable() {
			public void run() {
				doActualSearch(departure, arrival);
			}
		});
		thread.start();

	}

	private void doActualSearch(final String departure, final String arrival) {

		try {
			final ArrayList<PathRecord> tmp = reader.loadPath(departure,
					arrival);
			final ArrayList<ItineraryItem> itinerary = makeItinerary(
					modeAffichageVerbose, tmp);
			// TODO gerer le cas où l'un des stops est invalide
			if (tmp == null) {
				error(getResources().getString(R.string.error_computingpath));
			} else {
				// TODO cas ou il n'y a pas de chemin
				// Informations
				synchronized (handler) {
					handler.post(new Runnable() {
						public void run() {
							LinearLayout lay = (LinearLayout) findViewById(R.id.infoHeader);
							lay.setVisibility(View.VISIBLE);
							int total = tmp.get(tmp.size() - 1).getLength() / 60;
							TextView txtInfo1 = (TextView) findViewById(R.id.lblInfo1);
							String textFormat = getResources().getString(
									R.string.approx_time);
							String text = String.format(textFormat, total);
							txtInfo1.setText(Html.fromHtml(text));
							TextView txtInfo2 = (TextView) findViewById(R.id.lblInfo2);
							textFormat = getResources().getString(
									R.string.connections);
							text = String.format(textFormat, nbConnections);
							txtInfo2.setText(Html.fromHtml(text));
							// Dep and Arrival
							TextView txtTitle2 = (TextView) findViewById(R.id.lblTitle2);
							TextView txtTitle3 = (TextView) findViewById(R.id.lblTitle3);
							txtTitle2.setText(departure);
							txtTitle3.setText(arrival);
							ArrayAdapter<ItineraryItem> exPath = new ItineraryListAdapter(
									getApplication(), R.layout.list, itinerary);
							listPath.setAdapter(exPath);
							// hides dialog
							dlgProgress.dismiss();
						}
					});
				}
			}

		} catch (Exception ex) {
			dlgProgress.dismiss();
			Log.e(TAG, ex.getMessage() != null ? ex.getMessage() : ex
					.toString(), ex);
			error(getResources().getString(R.string.error_computingpath));
		}
	}

	public ArrayList<ItineraryItem> makeItinerary(boolean modeVerbose,
			ArrayList<PathRecord> paths) {
		ArrayList<ItineraryItem> retVal = new ArrayList<ItineraryItem>();

		if (!modeVerbose) {
			int i = 0;
			nbConnections = 0;
			LineRecord line;

			do {
				line = paths.get(i).getLine();
				ItineraryItem it = new ItineraryItem();
				// A chatelet Prendre la Ligne 1
				it.setLabel1(getResources().getString(R.string.at) + " "
						+ paths.get(i).getPrevious().getStopName() + " "
						+ getResources().getString(R.string.take_line) + " "
						+ line.getLineName());
				it.setData(paths.get(i).getPrevious());
				// Direction La Defense
				String label2 = getResources().getString(R.string.direction)
						+ " " + line.getTerminus();

				// keep going until there's a connection
				while (++i < paths.size()
						&& paths.get(i).getLine().getLineId() == line
								.getLineId())
					;
				// Descendre à Nation
				label2 += " - " + getResources().getString(R.string.get_off)
						+ " " + paths.get(i - 1).getArrival().getStopName();
				it.setLabel2(label2);
				it.addImage(R.drawable.metro);
				retVal.add(it);
				if (i < paths.size())
					nbConnections++;
			} while (i < paths.size());
		} else {

		}

		return retVal;
	}

	/**
	 * Loads the map data in the background
	 */
	private void loadMap() throws IOException {
		if (stopsList == null) {
			// first load map metadata
			reader.loadIndexes(false);
			synchronized (handler) {
				handler.post(new Runnable() {
					public void run() {
						// afficher les infos
						showMapMetadata(reader.getMetadata());
					}
				});
			}
			// load map indexes
			reader.loadIndexes(true);
			String[] t = new String[0];
			stopsList = reader.getStopsList().toArray(t);
		}

	}

	private void showMapMetadata(MetroidMetadata meta) {
		LinearLayout lay = (LinearLayout) findViewById(R.id.homeDisp);
		Context context = getApplication();
		TextView tv = new TextView(context);
		tv.setText(mapTitle+" "+meta.getMean());
		tv.setTextAppearance(context, R.style.darkBoldText);
		lay.addView(tv, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.WRAP_CONTENT));
		// Created by Author
		if (meta.getAuthor() != null) {
			tv = new TextView(context);
			tv.setText(getResources().getString(R.string.created_by) + " "
					+ meta.getAuthor());
			tv.setTextAppearance(context, R.style.darkBoldText);
			lay.addView(tv, new LinearLayout.LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
		}
		// Version (date)
		tv = new TextView(context);
		tv.setText(meta.getVersion());
		tv.setTextAppearance(context, R.style.darkBoldText);
		lay.addView(tv, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.WRAP_CONTENT));
		// Nb Lines, Nb Stops
		tv = new TextView(context);
		String tmp = "";
		tmp = meta.getNbLines() + " "
				+ getResources().getString(R.string.lines) + ", "
				+ meta.getNbStops() + " "
				+ getResources().getString(R.string.stops);
		tv.setText(tmp);
		tv.setTextAppearance(context, R.style.darkBoldText);
		lay.addView(tv, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.WRAP_CONTENT));
		// ok. make the whole thing visible
		LinearLayout homeLay = (LinearLayout) findViewById(R.id.homeLayout);
		homeLay.setVisibility(View.VISIBLE);
	}

	// Listen for results.
	protected void onActivityResult(int requestCode, int resultCode,
			String data, Bundle extras) {
		// See which child activity is calling us back.
		switch (requestCode) {
		case SEARCH_FIELDS:
			// This is the standard resultCode that is sent back if the
			// activity crashed or didn't doesn't supply an explicit result.
			if (resultCode == RESULT_CANCELED) {

			} else {
				if (extras != null) {
					departure = extras.getString(KEY_DEPARTURE);
					arrival = extras.getString(KEY_ARRIVAL);
					doSearch();
				}
			}
		default:
			break;
		}
	}

	public void bip(String p) {
		if (debugMode)
			new AlertDialog.Builder(MetroidCentral.this)
		.setTitle("Error")
		.setMessage(p)
		.setIcon(R.drawable.icon)
		.setPositiveButton("ok",
				new DialogInterface.OnClickListener() {
		public void onClick(DialogInterface dialog, int whichButton) {}})
		.show();
			//showAlert("Error", R.drawable.icon, p, "ok", false);
	}

	public void error(String msg) {
		
		/*showAlert(getResources().getString(R.string.app_name), R.drawable.icon,
				msg, getResources().getString(R.string.close), true);*/
		
		new AlertDialog.Builder(MetroidCentral.this)
		.setTitle(getResources().getString(R.string.app_name))
		.setMessage(msg)
		.setIcon(R.drawable.icon)
		.setPositiveButton(getResources().getString(R.string.close),
				new DialogInterface.OnClickListener() {
		public void onClick(DialogInterface dialog, int whichButton) {}})
		.show();
	}
	
	

	private class ItineraryListAdapter extends ArrayAdapter<ItineraryItem> {
		private Context mContext;
		private boolean[] mExpanded;

		public ItineraryListAdapter(Context context, int resource,
				ArrayList<ItineraryItem> items) {
			super(context, resource, items);
			mContext = context;
			mExpanded = new boolean[items.size()];
			// items are not expanded by default
			for (int i = 0; i < items.size(); i++)
				mExpanded[i] = false;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			ExpandableItineraryView ev;
			ItineraryItem rec = getItem(position);
			if (convertView == null) {
				ev = new ExpandableItineraryView(mContext, rec.getImages(), rec
						.getLabel1(), rec.getLabel2(), false);
			} else {
				ev = (ExpandableItineraryView) convertView;
				ev.setTitle(rec.getLabel1());
				ev.setDirection(rec.getLabel2());
				ev.addImages(rec.getImages());
				ev.setExpanded(mExpanded[position]);
			}

			return ev;
		}

		public void toggle(int position) {
			mExpanded[position] = !mExpanded[position];
			notifyDataSetChanged();
		}
	}

	private class ExpandableItineraryView extends LinearLayout {
		public ExpandableItineraryView(Context context,
				ArrayList<Integer> imgRes, String label1, String label2,
				boolean expanded) {
			super(context);

			this.setOrientation(HORIZONTAL);

			// Here we build the child views in code. They could also have
			// been specified in an XML file.

			LinearLayout innerLayout = new LinearLayout(context);
			innerLayout.setOrientation(VERTICAL);

			imgLayout = new LinearLayout(context);
			imgLayout.setOrientation(HORIZONTAL);
			for (Integer i : imgRes) {
				ImageView iv = new ImageView(getContext());
				iv.setImageResource(i);
				imgLayout.addView(iv, new LinearLayout.LayoutParams(
						LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
			}

			addView(imgLayout, new LinearLayout.LayoutParams(
					LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

			mTitle = new TextView(context);
			mTitle.setTextAppearance(context, R.style.darkBoldText);
			mTitle.setText(label1);
			mTitle.setPadding(0, 0, 0, 2);
			innerLayout.addView(mTitle, new LinearLayout.LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

			mDirection = new TextView(context);
			mDirection.setTextAppearance(context, R.style.darkBoldText);
			mDirection.setText(label2);
			mDirection.setPadding(0, 0, 0, 4);
			innerLayout.addView(mDirection, new LinearLayout.LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

			mDirection.setVisibility(expanded ? VISIBLE : GONE);

			addView(innerLayout, new LinearLayout.LayoutParams(
					LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
		}

		/**
		 * Convenience method to set the title of a SpeechView
		 */
		public void setTitle(String title) {
			mTitle.setText(title);
		}

		/**
		 * Convenience method to set the dialogue of a SpeechView
		 */
		public void setDirection(String words) {
			mDirection.setText(words);
		}

		public void addImage(int imgRes) {
			ImageView iv = new ImageView(getContext());
			iv.setImageResource(imgRes);
			imgLayout.addView(iv, new LinearLayout.LayoutParams(
					LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
		}

		public void addImages(ArrayList<Integer> imgRes) {
			imgLayout.removeAllViews();
			for (Integer i : imgRes) {
				ImageView iv = new ImageView(getContext());
				iv.setImageResource(i);
				imgLayout.addView(iv, new LinearLayout.LayoutParams(
						LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
			}
		}

		/**
		 * Convenience method to expand or hide the dialogue
		 */
		public void setExpanded(boolean expanded) {
			mDirection.setVisibility(expanded ? VISIBLE : GONE);
		}

		private TextView mTitle;
		private TextView mDirection;
		private LinearLayout imgLayout;
	}
}